import { createApp } from 'vue';
import { addClass, removeClass } from '@/assets/js/dom';

// 类名
const relativeCls = 'g-relative';

export default function createLoadingLikeDirective(Comp) {
  return {
    // 将组建动态插入到写了该指令的容器内部
    // 钩子函数
    // 指令挂载时执行
    // el为指令作用的DOM元素
    mounted(el, binding) {
      // 创建app应用,根组件为loading组件
      // 使用createApp方法返回组件实例
      const app = createApp(Comp);
      // 拿到Loading组件实例
      // 通过mount方法挂载在动态创建的DOM对象中(loading组件的对应的对象在instance上)
      // mount方法挂载的动态创建的div不会挂载到Body上,不会有DOM层上的挂载,因为要把Loading实例挂载在el上
      // 如果直接挂载具名的DOM元素上会覆盖掉DOM元素底下的内容,所以需要挂载到一个动态生成的div上,从而拿到组件的实例,追加到el的子元素
      // instance为传入组件的实例
      const instance = app.mount(document.createElement('div'));
      // 因为这个组件是公共的,会出现多个指令作用到同一元素上,会有多个组件使用这个代码,不同的组件用不同的el.name来存储instance,不会出现后面覆盖前面的情况
      const name = Comp.name;
      if (!el[name]) {
        el[name] = {};
      }

      // 动态参数
      // 通过binding.arg来获取传入的动态参数
      const title = binding.arg;
      // 如果有传入的动态参数,则设置动态参数
      if (typeof title !== 'undefined') {
        // instance为传入组件的实例
        // 调用传入的组件的方法
        instance.setTitle(title);
      }

      // 将instance保留在el对象上
      el[name].instance = instance;
      console.log(el[name].instance, 'el[name].instance');
      // 判断并执行挂载操作
      if (binding.value) {
        // 将loading组件创建出来的DOM对象挂载在loading指令作用的el上
        append(el);
      }
    },
    // 指令更新时执行
    updated(el, binding) {
      const name = Comp.name;
      const title = binding.arg;
      // 如果有传入的动态参数,则设置动态参数
      if (typeof title !== 'undefined') {
        el[name].instance.setTitle(title);
      }
      // loading前后的值不一致的话
      if (binding.value !== binding.oldValue) {
        // binding.value为true挂载组件,否则卸载组件
        binding.value ? append(el) : remove(el);
      }
    }
  };
  // 执行挂载
  function append(el) {
    const name = Comp.name;
    // 添加相对定位,以适应各种方式的布局,loading组件本身依赖绝对定位来居中对齐,需要使用指令的DOM元素绝对定位来实现,动态监测传入的DOM对象的定位样式来判断添加
    // 通过getComputedStyle拿到DOM元素的style
    const style = getComputedStyle(el);
    // 查看DOM元素是否有定位
    if (['absolute', 'fined', 'relative'].indexOf(style.position) === -1) {
      // 添加样式(添加类名)
      addClass(el, relativeCls);
    }
    // 拿到对应的DOM对象并挂载
    // el.instance为loading组件的实例
    // $el为loading组件对应的DOM对象
    // 将DOM对象挂载在loading组件作用的DOM上
    // v-loading作用在哪个DOM上参数的el就指向那个DOM
    el.appendChild(el[name].instance.$el);
  }

  // 卸载组件
  function remove(el) {
    const name = Comp.name;
    // 移除样式(类名)
    removeClass(el, relativeCls);
    el.removeChild(el[name].instance.$el);
  }
}
